import type React from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { DEFAULT_MAP_CENTER, DEFAULT_MAP_ZOOM } from '@/config/constants';
import type { RootState } from '@/store';
import popupStyles from '../assets/popup.module.less';
import { MapContext } from '../MapContext';
import Mapless from '../Mapless';
import type { MapStyle } from '../Mapless/index.d';
import Lang from '../Mapless/lang';
import styles from './with.module.less';
import 'mapbox-gl/dist/mapbox-gl.css';
import useSize from '../hooks/useSize';

export type WithMapChangeEvent = 'init' | 'zoom' | 'ready' | (string & {});

export type WithMapChangeEventArgs = {
  type: WithMapChangeEvent;
  target: Mapless;
  others?: Record<string, unknown>;
};

export type WithMapOptions = {
  accessToken?: string;
  style?: MapStyle;
  center?: [number, number];
  zoom?: number;
  lang?: string;
  onChange?: (e: WithMapChangeEventArgs) => void;
  resizable?: boolean;
  className?: string;
};

export type WithMapInjectedProps = {
  mapless: Mapless | undefined;
  maplessRef: React.RefObject<Mapless | undefined>;
  isMapReady: boolean;
  setMapState?: (key: 'styleName' | 'zoom', value: unknown) => void;
};

export type WithMapProps<T extends object> = WithMapOptions & T;

const withMap = <P extends WithMapInjectedProps>(
  WrappedComponent: React.ComponentType<P>,
) => {
  return (props: WithMapProps<Omit<P, keyof WithMapInjectedProps>>) => {
    const { style, resizable = false } = props;

    const [maplessInstance, setMaplessInstance] = useState<Mapless>();
    const [isMapReady, setIsMapReady] = useState(false);
    const maplessInstanceRef = useRef<Mapless>(undefined);
    const mapviewRef = useRef<HTMLDivElement>(null);
    const mapContainerRef = useRef<HTMLDivElement>(null);

    const langRef = useRef<Lang>(undefined);
    const lastStyleRef = useRef<string>(undefined);

    useSize({
      resizable,
      mapviewRef,
      mapContainer: mapContainerRef,
      maplessInstanceRef: maplessInstanceRef,
    });

    const themeType = useSelector((state: RootState) => state.theme.value);

    // TODO 移到mapless中处理
    const [withStyle, setWithStyle] = useState('');
    const setMapState = useCallback(
      (key: 'styleName' | 'zoom', value: unknown) => {
        if (key === 'styleName') {
          setWithStyle(value as string);
        }
      },
      [],
    );

    useEffect(() => {
      if (maplessInstance) {
        const handleStyle = langRef.current?.getLangStyle.bind(langRef.current);
        const themeStyle = getStyleName();
        if (lastStyleRef.current !== themeStyle) {
          maplessInstance.setStyle(themeStyle, handleStyle);
          lastStyleRef.current = themeStyle;
        }
      }
    }, [themeType, style, withStyle]);

    const { i18n } = useTranslation();
    useEffect(() => {
      i18n.on('languageChanged', (lng) => {
        const lang = lng === 'zh-CN' ? 'nonlatin' : 'latin';
        langRef.current?.switchDzmapLang(lang);
      });
    }, [i18n]);

    const getStyleName = useCallback(() => {
      let themeStyle = withStyle || style || 'streets';
      if (
        ![
          'dz_satellite',
          'dz_streets',
          'dz_cyber',
          'dz_light',
          'dz_dark',
        ].includes(themeStyle.toString())
      ) {
        if (themeType) {
          themeStyle = themeType === 'dark' ? 'dz_dark' : 'dz_streets';
        }
      }
      return themeStyle;
    }, [themeType, style, withStyle]);

    useEffect(() => {
      if (!mapContainerRef.current) return;

      const themeStyle = getStyleName();

      const instance = new Mapless(mapContainerRef.current, {
        accessToken: props.accessToken,
        style: themeStyle,
        center: props.center || DEFAULT_MAP_CENTER,
        zoom: props.zoom || DEFAULT_MAP_ZOOM,
        onMapInit() {
          props.onChange?.({
            type: 'init',
            target: instance,
          });

          const lang = i18n.language === 'zh-CN' ? 'nonlatin' : 'latin';
          langRef.current = new Lang(instance, lang, ':');
        },
        onMapReady() {
          setMaplessInstance(instance);
          maplessInstanceRef.current = instance;
          setIsMapReady(true);
          props.onChange?.({ type: 'ready', target: instance });
        },
        onMapZoom(zoom) {
          props.onChange?.({
            type: 'zoom',
            target: instance,
            others: { zoom },
          });
        },
      });

      return () => {
        try {
          instance.destroy();
          setMaplessInstance(undefined);
          maplessInstanceRef.current = undefined;
        } catch (error) {
          console.error('Mapless instance destroy error:', error);
        }
      };
    }, [props.accessToken]);

    return (
      <MapContext.Provider value={{ mapless: maplessInstance, isMapReady }}>
        <div
          ref={mapviewRef}
          className={`${styles.MapView} ${popupStyles['map-popup']} ${props.className}`}
        >
          <div ref={mapContainerRef} className={styles.map} />
          <WrappedComponent
            {...(props as P)}
            mapless={maplessInstance}
            maplessRef={maplessInstanceRef}
            isMapReady={isMapReady}
            setMapState={setMapState}
          />
        </div>
      </MapContext.Provider>
    );
  };
};

export default withMap;
